/*------------------------------------------------------------------------------
 * Copyright 2012 NCR Corporation
 *------------------------------------------------------------------------------
 * All revision information is updated automatically from source code control
 * change records - please do not manually edit.
 *------------------------------------------------------------------------------
 *
 * $LastChangedRevision:: 2104                                     $
 * $LastChangedBy:: rb250096                                       $
 * $LastChangedDate:: 2012-04-16 16:58:17 -0400 (Mon, 16 Apr 2012) $
 */

package com.ncr.nep.dashboardcore.modularization;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

import com.google.gwt.core.ext.Generator;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.NotFoundException;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
import com.ncr.nep.dashboardcore.modularization.client.SplitPoint;

/**
 * Deferred-binding code {@link Generator} that is used to automatically
 * create a
 * {@link com.ncr.nep.dashboardcore.modularization.client.SplitPointRegistryInitializer}
 * implementation that adds each detected {@link SplitPoint} on the classpath
 * at compile-time to the
 * {@link com.ncr.nep.dashboardcore.modularization.client.SplitPointRegistry}.
 *
 * @author rb250096
 * @version $Revision: 2104 $
 */
public class SplitPointRegistryInitializerCodeGenerator extends Generator {

    @Override
    public String generate(TreeLogger logger,
                           GeneratorContext context,
                           String typeName) throws UnableToCompleteException {
        try {
            //Get class type of SplitPointRegistryInitializer
            JClassType classType =
                    context.getTypeOracle().getType(typeName);

            JClassType splitPointType = context.getTypeOracle().getType(
                    SplitPoint.class.getName());
            ArrayList<JClassType> splitPoints = new ArrayList<JClassType>();
            JClassType[] allTypes = context.getTypeOracle().getTypes();
            for(JClassType type : allTypes) {
                if(splitPointType.isAssignableFrom(type) &&
                        !splitPointType.equals(type) &&
                        !type.isAbstract()) {
                    splitPoints.add(type);
                }
            }

            SourceWriter sw = this.newSourceWriter(classType, splitPoints,
                    context, logger);

            //Depending on the phase of the GWT compiler, the SourceWriter
            //instance might not be created due to a null value being returned
            //by GeneratorContext. In this case, simply ignore and return the
            //generated class name; there is no way around this logic as it is
            //part of the GWT compiler.
            if(sw != null) {
                sw.println("@Override");
                sw.println("public void init() {");
                if(splitPoints.size() > 0) {
                    sw.indent();
                    sw.println("SplitPointRegistry.register(");
                    sw.indent();
                    for(int i = 0, l = splitPoints.size(); i < l; i++) {
                        JClassType splitPoint = splitPoints.get(i);
                        sw.println(String.format("new %s()%s",
                                splitPoint.getSimpleSourceName(),
                                i < l - 1 ? "," : ""));
                    }
                    sw.outdent();
                    sw.println(");");
                    sw.outdent();
                }
                sw.println("}");
                sw.commit(logger);
            }

            return typeName + "Generated";
        }
        catch(NotFoundException nEx) {
            nEx.printStackTrace();
            return null;
        }
    }

    /**
     * Generates a new {@link SourceWriter} that is used to generate the new
     * {@link com.ncr.nep.dashboardcore.modularization.client.SplitPointRegistryInitializer}
     * instance.
     *
     * @param classType The class type for the
     *                  {@link com.ncr.nep.dashboardcore.modularization.client.SplitPointRegistryInitializer}
     *                  interface.
     *
     * @param splitPoints List of {@link SplitPoint} instances found on the
     *                    classpath.
     * @param context The generator context.
     * @param logger The logging instance.
     *
     * @return The new {@link SourceWriter} instance or null if one cannot
     * be created.
     */
    protected SourceWriter newSourceWriter(JClassType classType,
                                           List<JClassType> splitPoints,
                                           GeneratorContext context,
                                           TreeLogger logger) {
        String packageName = classType.getPackage().getName();
        String className = classType.getSimpleSourceName() + "Generated";
        ClassSourceFileComposerFactory composer =
                new ClassSourceFileComposerFactory(packageName, className);
        composer.addImplementedInterface(classType.getName());

        for(JClassType splitPoint : splitPoints) {
            String splitPointName = splitPoint.getPackage().getName() + "." +
                    splitPoint.getName();
            composer.addImport(splitPointName);
        }

        PrintWriter printWriter = context.tryCreate(logger, packageName,
                className);
        return printWriter != null ?
                composer.createSourceWriter(context, printWriter) : null;
    }
}
